"""Run inference with a YOLOv5 model on images, videos, directories, streams

Usage:
    $ python path/to/detect.py --source path/to/img.jpg --weights yolov5s.pt --img 640
"""

import argparse
import sys
import time
from pathlib import Path
import numpy as np
import cv2
import torch
import torch.backends.cudnn as cudnn
from queue import Queue

FILE = Path(__file__).absolute()
sys.path.append(FILE.parents[0].as_posix())  # add yolov5/ to path

from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import check_img_size, check_requirements, check_imshow, colorstr, non_max_suppression, \
    apply_classifier, scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path, save_one_box
from utils.plots import colors, plot_one_box
from utils.torch_utils import select_device, load_classifier, time_synchronized
save_name=0
def letterbox(img, new_shape=(640, 640), color=(114, 114, 114), auto=True, scaleFill=False, scaleup=True, stride=32):
    # Resize and pad image while meeting stride-multiple constraints
    shape = img.shape[:2]  # current shape [height, width]
    if isinstance(new_shape, int):
        new_shape = (new_shape, new_shape)

    # Scale ratio (new / old)
    r = min(new_shape[0] / shape[0], new_shape[1] / shape[1])
    if not scaleup:  # only scale down, do not scale up (for better test mAP)
        r = min(r, 1.0)

    # Compute padding
    ratio = r, r  # width, height ratios
    new_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
    dw, dh = new_shape[1] - new_unpad[0], new_shape[0] - new_unpad[1]  # wh padding
    if auto:  # minimum rectangle
        dw, dh = np.mod(dw, stride), np.mod(dh, stride)  # wh padding
    elif scaleFill:  # stretch
        dw, dh = 0.0, 0.0
        new_unpad = (new_shape[1], new_shape[0])
        ratio = new_shape[1] / shape[1], new_shape[0] / shape[0]  # width, height ratios

    dw /= 2  # divide padding into 2 sides
    dh /= 2

    if shape[::-1] != new_unpad:  # resize
        img = cv2.resize(img, new_unpad, interpolation=cv2.INTER_LINEAR)
    top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))
    left, right = int(round(dw - 0.1)), int(round(dw + 0.1))
    img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value=color)  # add border
    return img, ratio, (dw, dh)

def run_detect(img_queue,device,half,model,stride,imgsz,names):
    conf_thres=0.25   # confidence threshold
    iou_thres=0.45   # NMS IOU threshold
    max_det=1000   # maximum detections per image
    classes=None   # filter by class: --class 0, or --class 0 2 3
    agnostic_nms=False   # class-agnostic NMS
    augment=False   # augmented inference
    #####################
    im0s = img_queue
    # Padded resize
    img = letterbox(im0s,imgsz,stride=stride)[0]

    # Convert
    img = img[:, :, ::-1].transpose(2, 0, 1)  # BGR to RGB and HWC to CHW
    img = np.ascontiguousarray(img)
    #####################
    
    #t0 = time.time()
    
    img = torch.from_numpy(img).to(device)
    img = img.half() if half else img.float()  # uint8 to fp16/32
    img /= 255.0  # 0 - 255 to 0.0 - 1.0
    if img.ndimension() == 3:
        img = img.unsqueeze(0)

    # Inference
    #t1 = time_synchronized()
    pred = model(img, augment=augment)[0]

    # Apply NMS
    pred = non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
    #t2 = time_synchronized()
    #print(f'detect time:{t2 - t1:.3f}s)')

    # Process detections
    for i, det in enumerate(pred):  # detections per image

        s, im0 =  '', im0s.copy()

        if len(det):
            # Rescale boxes from img_size to im0 size
            det[:, :4] = scale_coords(img.shape[2:], det[:, :4], im0.shape).round()

            # Print results
            # print("log:\r\n")
            for c in det[:, -1].unique():
                n = (det[:, -1] == c).sum()  # detections per class
                s += f"{n} {names[int(c)]}{'s' * (n > 1)}, "  # add to string
                # print('wn')
                # print('c',c)
                # print('n',c)
                # print(s)

            ##Write results
            for *xyxy, conf, cls in reversed(det):
                c = int(cls)  # integer class
                label = names[c]
                plot_one_box(xyxy, im0, label=label, color=colors(c, True), line_thickness=3)

        #t3 = time_synchronized()
        #print(f'{s}Done. ({t3 - t1:.3f}s)')

        # Stream results
        #if view_img:
        # cv2.imshow("wn", im0)
        # cv2.waitKey(1000)  # 1000 millisecond
        global save_name
        save_name=save_name+1
        savepath=f".\data\images\{save_name}{'.'}{'jpg'}"
        cv2.imwrite(savepath, im0)
        

    #print(f'Done. ({time.time() - t0:.3f}s)')
    return s


@torch.no_grad()
def run_init():
    check_requirements(exclude=('tensorboard', 'thop'))
    ######    
    weights='yolov5s.pt'  # model.pt path(s)
    imgsz=640   # inference size (pixels)

    device=''   # cuda device, i.e. 0 or 0,1,2,3 or cpu

    half=False      # use FP16 half-precision inference

    # Initialize
    set_logging()
    device = select_device(device)
    half &= device.type != 'cpu'  # half precision only supported on CUDA

    # Load model
    model = attempt_load(weights, map_location=device)  # load FP32 model
    stride = int(model.stride.max())  # model stride
    imgsz = check_img_size(imgsz, s=stride)  # check image size
    names = model.module.names if hasattr(model, 'module') else model.names  # get class names
    print("class:",names)
    if half:
        model.half()  # to FP16

    # Run inference
    if device.type != 'cpu':
        model(torch.zeros(1, 3, imgsz, imgsz).to(device).type_as(next(model.parameters())))  # run once
    return device,half,model,stride,imgsz,names

def main():
    # check_requirements(exclude=('tensorboard', 'thop'))

    image_q = Queue(maxsize=0)  #创建队列FIFO
    
    imge = cv2.imread("./data/images/zidane.jpg")  # BGR
    image_q.put(imge) #写入队列

    imge = cv2.imread("./data/images/a.jpeg")  # BGR
    image_q.put(imge) #写入队列

    device,half,model,stride,imgsz,names=run_init()
    
    flag=1
    while flag:
        if(image_q.empty()==False):
            imge=image_q.get()
            result_s=run_detect(imge,device,half,model,stride,imgsz,names)
            print(result_s)
        else:
            print("imge queue is empty\r\n")
            flag=0


if __name__ == "__main__":
    main()
